package com.indexingsystem.db.mongoDB.comon;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

import org.apache.commons.beanutils.BeanUtils;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.bson.types.ObjectId;

import com.indexingsystem.boss.utils.DateUtil;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;

public class MongoDBUtils {

	// 将查询的数据设置到bean上
	public static <T> List<T> data2Bean(Class<T> clazz, MongoCursor<Document> cursor) {
		try {

			List<T> array = new LinkedList<T>();
			while (cursor.hasNext()) {
				Document doc = cursor.next();
				T bean = clazz.newInstance();
				Field[] fields = clazz.getDeclaredFields();
				if (fields != null) {
					for (Field field : fields) {
						field.setAccessible( true );
						setData( bean, field, doc );
					}
					array.add( bean );
				}

			}

			return array;

		} catch (Exception e) {
			throw new RuntimeException( e );
		} finally {
			if (cursor != null) {
				cursor.close();
			}
		}
	}

	// 传入bean返回Document进行插入
	public static <T> Document transform2Doc(T bean) throws Exception {
		Class clazz = bean.getClass();
		HashMap<String, Object> data = new HashMap<String, Object>();
		Field[] fields = clazz.getDeclaredFields();
		if (fields != null) {
			for (Field field : fields) {
				field.setAccessible( true );
				String fname = field.getName();
				if (fname.equals( "id" ))
					continue;
				if (fname.equals( "serialVersionUID" ))
					continue;
				Object value = field.get( bean );
				data.put( fname, value );
			}
		}
		return new Document( data );
	}

	private static <T> void setData(T bean, Field field, Document doc) throws Exception {
		String type = field.getType().getSimpleName();
		String name = field.getName();
		if ("String".equals( type ) && "id".equals( name )) {
			ObjectId id = doc.getObjectId( "_id" );
			String data = id.toString();
			field.set( bean, data );
		} else if ("Double".equals( type )) {
			Double data = doc.getDouble( name );
			field.set( bean, data );
		} else if ("String".equals( type )) {
			String data = doc.getString( name );
			field.set( bean, data );
		} else if ("Date".equals( type )) {
			Date data = doc.getDate( name );
			field.set( bean, data );
		} else if ("Boolean".equals( type )) {
			Boolean data = doc.getBoolean( name );
			field.set( bean, data );
		} else if ("Integer".equals( type )) {
			String value = null;
			try {
				value = doc.getDouble( name ) + "";
			} catch (ClassCastException e) {
				value = doc.getInteger( name ) + "";
			}
			int index = value.lastIndexOf( "." );
			if (index > -1)
				value = value.substring( 0, index );
			Integer data = Integer.parseInt( value );
			field.set( bean, data );
		} else if ("Long".equals( type )) {
			String value = null;
			try {
				value = doc.getDouble( name ) + "";
			} catch (ClassCastException e) {
				value = doc.getLong( name ) + "";
			}
			int index = value.lastIndexOf( "." );
			if (index > -1)
				value = value.substring( 0, index );
			Long data = Long.parseLong( value );
			field.set( bean, data );
		} else if ("Float".equals( type )) {
			Float data = Float.parseFloat( doc.getDouble( name ) + "" );
			field.set( bean, data );
		}
	}

	/**
	 * 把实体bean对象转换成DBObject
	 * 
	 * @param bean
	 * @return
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 */
	public static <T> DBObject bean2DBObject(T bean) throws IllegalArgumentException, IllegalAccessException {
		if (bean == null) {
			return null;
		}
		DBObject dbObject = new BasicDBObject();
		// 获取对象对应类中的所有属性域
		Field[] fields = bean.getClass().getDeclaredFields();
		for (Field field : fields) {
			// 获取属性名
			String varName = field.getName();
			// 修改访问控制权限
			boolean accessFlag = field.isAccessible();
			if (!accessFlag) {
				field.setAccessible( true );
			}
			Object param = field.get( bean );
			if (param == null) {
				continue;
			} else if (param instanceof Integer) {// 判断变量的类型
				int value = ((Integer) param).intValue();
				dbObject.put( varName, value );
			} else if (param instanceof String) {
				String value = (String) param;
				dbObject.put( varName, value );
			} else if (param instanceof Double) {
				double value = ((Double) param).doubleValue();
				dbObject.put( varName, value );
			} else if (param instanceof Float) {
				float value = ((Float) param).floatValue();
				dbObject.put( varName, value );
			} else if (param instanceof Long) {
				long value = ((Long) param).longValue();
				dbObject.put( varName, value );
			} else if (param instanceof Boolean) {
				boolean value = ((Boolean) param).booleanValue();
				dbObject.put( varName, value );
			} else if (param instanceof Date) {
				Date value = (Date) param;
				dbObject.put( varName, value );
			}
			// 恢复访问控制权限
			field.setAccessible( accessFlag );
		}
		return dbObject;
	}

	/**
	 * 把DBObject转换成bean对象
	 * 
	 * @param dbObject
	 * @param bean
	 * @return
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 * @throws NoSuchMethodException
	 */
	public static <T> T dbObject2Bean(DBObject dbObject, T bean) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
		if (bean == null) {
			return null;
		}
		Field[] fields = bean.getClass().getDeclaredFields();
		for (Field field : fields) {
			String varName = field.getName();
			Object object = dbObject.get( varName );
			if (object != null) {
				BeanUtils.setProperty( bean, varName, object );
			}
		}
		return bean;
	}

	// /**
	// * 根据查询的记录转成javabean
	// *
	// * @param collection
	// * @param filter
	// * @param obj
	// * @return
	// */
	// public List queryToBeanList(MongoCollection<Document> collection, Bson
	// filter, Object obj) {
	// List list = new ArrayList();
	// MongoCursor<Document> cursor = null;
	// if (filter != null) {
	// cursor = collection.find( filter ).iterator();
	// } else {
	// cursor = collection.find().iterator();
	// }
	//
	// while (cursor.hasNext()) {
	// Document doc = cursor.next();
	// Object object;
	// try {
	// object = ReflectUtil.setFieldValue( obj.getClass().newInstance(), doc );
	// list.add( object );
	// } catch (NoSuchMethodException e) {
	// e.printStackTrace();
	// } catch (SecurityException e) {
	// e.printStackTrace();
	// } catch (IllegalAccessException e) {
	// e.printStackTrace();
	// } catch (IllegalArgumentException e) {
	// e.printStackTrace();
	// } catch (InvocationTargetException e) {
	// e.printStackTrace();
	// } catch (InstantiationException e) {
	// e.printStackTrace();
	// }
	// }
	// return list;
	// }

	/**
	 * 根据mongodb中的document记录设置对应的实体类的属性
	 * 
	 * @param obj
	 * @param doc
	 * @return
	 * @throws NoSuchMethodException
	 * @throws SecurityException
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 * @throws InvocationTargetException
	 * @throws InstantiationException
	 */
	public static Object setFieldValue(Object obj, Document doc) throws NoSuchMethodException, SecurityException, IllegalAccessException,
			IllegalArgumentException, InvocationTargetException, InstantiationException {
		Class clazz = obj.getClass();
		Field[] fields = clazz.getDeclaredFields();
		Method[] methods = clazz.getDeclaredMethods();
		for (Field field : fields) {
			String name = field.getName();
			String setName = parseSetName( name );
			if (!checkSetMethod( methods, setName )) {
				continue;
			}
			String fieldType = field.getType().getName();
			Method fieldSetMet = clazz.getDeclaredMethod( setName, field.getType() );
			if ("java.lang.String".equalsIgnoreCase( fieldType )) {
				String value = doc.getString( name );
				if (value == null || "".equals( value )) {
					continue;
				}
				fieldSetMet.invoke( obj, value );
			} else if ("java.lang.Integer".equalsIgnoreCase( fieldType ) || "int".equalsIgnoreCase( fieldType )) {
				Integer value = doc.getInteger( name );
				if (value == null) {
					continue;
				}
				fieldSetMet.invoke( obj, value );
			} else if ("java.lang.Long".equalsIgnoreCase( fieldType ) || "long".equalsIgnoreCase( fieldType )) {

				Long value = doc.getLong( name );
				if (value == null) {
					continue;
				}
				fieldSetMet.invoke( obj, value );
			} else if ("java.lang.Double".equalsIgnoreCase( fieldType ) || "long".equalsIgnoreCase( fieldType )) {
				Double value = doc.getDouble( name );
				if (value == null) {
					continue;
				}
				fieldSetMet.invoke( obj, value );
			} else if ("java.lang.Boolean".equalsIgnoreCase( fieldType ) || "boolean".equalsIgnoreCase( fieldType )) {
				Boolean value = doc.getBoolean( name );
				if (value == null) {
					continue;
				}
				fieldSetMet.invoke( obj, value );
			} else if ("java.sql.Timestamp".equalsIgnoreCase( fieldType )) {
				Date date = doc.getDate( name );
				if (date == null) {
					continue;
				}
				fieldSetMet.invoke( obj, new java.sql.Timestamp( date.getTime() ) );
			} else if ("java.sql.Date".equalsIgnoreCase( fieldType )) {
				Date date = doc.getDate( name );
				if (date == null) {
					continue;
				}
				fieldSetMet.invoke( obj, new java.sql.Date( date.getTime() ) );
			} else if ("java.util.Date".equalsIgnoreCase( fieldType )) {
				Date date = null;
				try {
					date = doc.getDate( name );
				} catch (Exception ex) {
					date = new Date( doc.getString( name ) );
				}
				if (date == null) {
					continue;
				}
				fieldSetMet.invoke( obj, date );
			} else if ("java.util.List".equalsIgnoreCase( fieldType )) {
				List<Document> docs = (List<Document>) doc.get( name );
				if (docs == null || docs.size() == 0) {
					continue;
				}
				Class fieldClazz = field.getType(); // 得到field的class及类型全路径+
				Class genericClazz = null;
				if (fieldClazz.isAssignableFrom( List.class )) //
				{
					Type fc = field.getGenericType(); // 关键的地方，如果是List类型，得到其Generic的类型

					if (fc instanceof ParameterizedType) // 如果是泛型参数的类型
					{
						ParameterizedType pt = (ParameterizedType) fc;

						genericClazz = (Class) pt.getActualTypeArguments()[ 0 ]; // 得到泛型里的class类型对象。

					}
				}
				List list = new ArrayList<>();
				for (Document document : docs) {
					Object genericobj = genericClazz.newInstance();
					Object object = setFieldValue( genericobj, document );
					list.add( object );
				}
				fieldSetMet.invoke( obj, list );
			} else {
				String docName = field.getType().getSimpleName();
				Document document = (Document) doc.get( docName );
				if (document == null) {
					continue;
				}
				Class fieldClazz = field.getType();
				Object object = fieldClazz.newInstance();
				object = setFieldValue( object, document );
				fieldSetMet.invoke( obj, object );
			}
		}
		return obj;
	}

	/**
	 * 检查实体类中是否有set方法
	 * 
	 * @param methods
	 * @param methodName
	 * @return
	 */
	public static boolean checkSetMethod(Method[] methods, String methodName) {
		for (Method method : methods) {
			if (methodName.equals( method.getName() )) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 根据属性名字拼接set方法名
	 * 
	 * @param name
	 * @return
	 */
	public static String parseSetName(String name) {
		if (name == null || "".equals( name )) {
			return null;
		}
		return "set" + name.substring( 0, 1 ).toUpperCase() + name.substring( 1 );
	}
}